package internet;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Site ranking servlet.
 * @author Marcelo Korn & Oleg Ruchlis
 */
@WebServlet(name = "servlet", urlPatterns = {"/servlet"})
public class servlet extends HttpServlet {

    /**
     *  stores the path to the file with the URLs list.
     */
    private String      inputFilePath;
    
    /**
     * an array of strings that will contain the URLs
     */ 
    private ArrayList   listOfUrls      = new ArrayList();
    
    /**
     * the ranking value for each URL
     */ 
    private double[]    rankingsValues  = null;
    
    /**
     * the number of rankings for each value (necessary for average calculation)
     */
    private int[]       numOfRankings   = null;
    
    /**
     * indicates a problem in ranking input for a certain entry (URL)
     */
    private boolean[]   rankingErrors   = null;
    
    /**
     * indicates a problem in form initiation
     */
    private boolean     initProblem     = false;
    
    /**
     * stores an error message from servlet initialization
     */
    private String      initErrorMeassage;
    
    /**
     * newline char (platform independent).
     */
    private static String newline = System.getProperty("line.separator");

    /**
     * initializes the servlet with default values and data structures
     * using a parameter from the xml config file
     * @throws ServletException when servlet load fails.
     */
    @Override
    public void init() throws ServletException {
        super.init();
        
        // printing current status to the log file.
        String dateAndTime = getDateAndTime();
        getServletContext().log(dateAndTime + "Servlet is loading." + newline);
        
                        // The relative (full) path
        inputFilePath = getServletContext().getRealPath("") +
                        // platform independent path seperator
                        File.separatorChar +
                        // the file name provided in inputFile parameter
                        getServletConfig().getInitParameter("inputFile").toString();
        
        FileInputStream fstream = null;

        // trting to open the input file for reading.
        try {
            fstream = new FileInputStream(inputFilePath);
        } catch (FileNotFoundException ex) {
            initProblem = true;
            initErrorMeassage = "Couldn't open the path provided in web.xml: " +
                                inputFilePath + newline +
                                "Please make sure to provide a valid file " +
                                "in the path stated above.";
            // printing the error to the log
            dateAndTime = getDateAndTime();
            getServletContext().log(dateAndTime + "Error occured! " + 
                                    initErrorMeassage + newline);
        }
        
        if(!initProblem){
            // enabling readLine() through buffered reader.
            DataInputStream in = new DataInputStream(fstream);
            BufferedReader br = new BufferedReader(new InputStreamReader(in));

            // reading line by line from the imput file (asuming valid URLs)
            String strLine = null;
            try {
                // putting the urls in the list.
                while((strLine = br.readLine()) != null)
                {
                    // trimming white spaces
                    strLine.trim();
                    // adding to list of URLs
                    listOfUrls.add(strLine);
                }
            } catch (IOException ex) {
            }

            // creating the arrays with the right sizes (number of URLs)
            rankingsValues  = new double[listOfUrls.size()];
            numOfRankings   = new int[listOfUrls.size()];
            rankingErrors   = new boolean[listOfUrls.size()];

            // closing all readers
            try{
                br.close();
                in.close();
                fstream.close();
            }catch(IOException ex){
            }
        }
    }
    
    /** 
     * Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.<br>
     * Creates the main web page.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        
        PrintWriter out = response.getWriter();
       
        try {
            // reading input from the form
            handleInput(request);
            
            // setting the doctype
            out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"" +
                        "\r\n" +
                        "\"http://www.w3.org/TR/html4/loose.dtd\">");
            // creating the web page (HTML) 
            out.println("<html>");
            
            // creating the HEAD section
            out.println("<head>");
            out.println("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">");
            out.println("<title>Exercise No3 by Marcelo Korn & Oleg Ruchlis</title>");  
            // setting the css
            out.println("<link rel=\"stylesheet\" type=\"text/css\" href=\"ex3_css.css\" title=\"myStyle\">");
            out.println("</head>");
            
            // Checking for problems in servlet intiation.
            if(initProblem){
                HandleInitProblem(out);
                return;
            }
            
            //starting the html body
            out.println("<body>");
            // web page titles
            out.println("<h1>Welcome to our site ranking:</h1>");
            out.println("<h2>Please rank the following web sites:</h2>");
            
            //starting the form.
            out.println("<FORM ACTION=\"servlet\" METHOD=\"GET\">");
            // creating a teable for the form
            out.println("<table>");
            out.println("<tr>");
            out.println("<th>");
            // first column is the list of web pages
            out.println("Web site");
            out.println("</th>");
            out.println("<th>");
            // second column is a text box for user's input
            out.println("Your grade");
            out.println("</th>");
            out.println("</tr>");

            // crating table entries (rows) for each URL in the list
            for(int i = 0 ; i < listOfUrls.size() ; i++){
                out.println("<tr>");
                out.println("<td>");
                // first column is the link + name of the web page.
                out.println("<a href=\"" + (String)listOfUrls.get(i) + "\" target=\"_blank\">" + (String)listOfUrls.get(i)+ "</a>");
                out.println("</td>");
                out.println("<td>");
                // second column is the text box.
                out.println("<INPUT TYPE=\"TEXT\" SIZE=\"1\" NAME=\"param" + (i+1) +"\">");
                out.println("</td>");
                
                // if user entered an error for this site at the last time 
                // an error message will be shown
                if(rankingErrors[i]){
                    out.println("<td>");
                    out.println("Invalid value! please insert a value between 0 and 10");
                    out.println("</td>");
                }
                
                out.println("</tr>");
            }
            
            // creating a row for the "Submit" button of the form
            out.println("<tr>");
            out.println("<td colspan=\"2\" align=\"center\">");
            out.println("<div align=\"center\">");
            out.println("<INPUT TYPE=\"SUBMIT\">");
            out.println("</div>");
            out.println("</td>");
            out.println("</tr>");
            out.println("</table>");
            out.println("</FORM>");
            
            // title for the results section
            out.println("<br>\r\n<br>");
            out.println("<h2>Ranking achieved so far:</h2>");
            out.println("<br>");
            
            // showing the number of votes till now
            out.println("<p>");
            out.println("Number of votes: " + getNumberOfVotes());
            out.println("</p>");
            
            // starting the results table
            out.println("<table>");
            out.println("<tr>");
            out.println("<th>");
            // first column is the list of web sites + links
            out.println("Web site");
            out.println("</th>");
            out.println("<th>");
            // second column is the grade bar
            out.println("Average grade");
            out.println("</th>");
            out.println("<th>");
            // third colum is the average grade in numerical value
            out.println("#");
            out.println("</th>");
            out.println("</tr>");
            
            // creating the table entries (urls)
            for(int i = 0 ; i < listOfUrls.size() ; i++)
            {
                out.println("<tr>");
                out.println("<td>");
                // name of the site + link
                out.println("<a href=\"" + (String)listOfUrls.get(i) + "\" target=\"_blank\">" + (String)listOfUrls.get(i)+ "</a>");
                out.println("</td>");
                // setting the color of the ranking bar
                out.println("<td><FONT COLOR=\"#6A0DFF\">");
                
                // Creating the ranking bar (single white space for each grade unit
                for(int j = 0 ; j < rankingsValues[i] ; j++)
                {
                    out.println("&nbsp");
                }
                 
                out.println("</FONT></td>");
                
                // printing the numerical value of the site's average grade.
                out.println("<td>");
                out.println(rankingsValues[i]);
                out.println("</td>");
                out.println("</tr>");
            }
            
            out.println("</table>");
            
            out.println("</body>");
            out.println("</html>");
        // closing the site writer
        } finally {            
            out.close();
        }
    }

    // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
    /** 
     * Handles the HTTP <code>GET</code> method.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /** 
     * Handles the HTTP <code>POST</code> method.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /** 
     * Returns a short description of the servlet.
     * @return a String containing servlet description
     */
    @Override
    public String getServletInfo() {
        return "Site ranking servlet";
    }// </editor-fold>

    /**
     * Checks the number of committed votes and turns it into string
     * @return String - the total number of votes.
     */
    private String getNumberOfVotes() {

        int votes = 0;
        
        if(numOfRankings.length > 0)
            votes = numOfRankings[0];
        
        return Integer.toString(numOfRankings[0]);
    }
    
    /**
     * Handles users input from the form and translates it into usable values<br>
     * calculates the average grade as well.
     * @param request the servlet with the input form.
     */
    private void handleInput(HttpServletRequest request)
    {
        // array for input values recieved in the form
        int[] inputValues = new int[listOfUrls.size()];
        
        // indicaind problem with one or more fields of the input
        boolean inputProblem = false;
        
        // checking input correctnes.
        for(int i = 0 ; i < listOfUrls.size() ; i++)
        {
            inputValues[i] = 0;
            rankingErrors[i] = false;

            try
            {
                // readin a text box into a string.
                String temp = request.getParameter("param" + (i+1));
                // handling first run of the servlet
                if(temp == null)
                    return;
                // transloating the string into an int
                inputValues[i] = Integer.parseInt(temp);
            }
            // incase of parsing problem (not a numerical value)
            catch(NumberFormatException ex)
            {
                rankingErrors[i] = true;
                inputProblem = true;
                continue;
            }
            
            // checking the range of the grade (must be between 0 and 10).
            if(inputValues[i] < 0 || inputValues[i] > 10){
                rankingErrors[i] = true;
                inputProblem = true;
            }
        }
        
        // if a problem occured no need to proccess the input
        if(inputProblem)
            return;
        
        // calculating the new average for each url.
        for(int i = 0 ; i < listOfUrls.size() ; i++)
        {
            // calculating the average.
            rankingsValues[i] = (rankingsValues[i]*numOfRankings[i] + inputValues[i]) / (numOfRankings[i]+1);
            numOfRankings[i]++;
        }
    }

    /**
     * creates a site describing the error happened in servlet initialization
     * @param out the web site writer
     */
    private void HandleInitProblem(PrintWriter out) {
        
        out.println("<body>");
        out.println("<h1>Error occured while loading the servlet!</h1>");
        out.println("<br>");
        out.println("<h2>");
        String[] tokens = initErrorMeassage.split(newline);
        for(String line : tokens){
            out.println(line);
            out.println("<br>");
        }
        out.println("Please fix this problem and restart the servlet");
        out.println("</h2>");
        out.println("</body>");
        out.println("</html>");
        return;
    }

    /**
     * Creates a string containing current date + time.
     */
    private String getDateAndTime() {
        
        Calendar calendar = new GregorianCalendar();
        
        String dateAndTime = calendar.get(Calendar.DAY_OF_MONTH) + "/" +
                             calendar.get(Calendar.MONTH) + "/" +
                             calendar.get(Calendar.YEAR) + "  " +
                             calendar.get(Calendar.HOUR_OF_DAY) + ":" +
                             calendar.get(Calendar.MINUTE)+ " -> ";
        
        return dateAndTime;
    }
}
